package jpa.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.tomcat.util.http.fileupload.FileItem;

/**
 * @author Remco Haszing
 */
@Entity
@Table(name = "Item")
public class Item implements Serializable, Cloneable {

    // properties
    private static final long serialVersionUID = 173111L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private ItemType type;
    @ManyToOne
    private Item parent;
    @OneToMany(cascade = CascadeType.PERSIST)
    private List<Item> children;
    private String title;
    private String shortDescription;
    private String longDescription;
    @OneToMany(cascade = CascadeType.PERSIST)
    List<Revision> revisions;

    // constructors
    /**
     * This constructor is used by the JPA API and should not be used for
     * anything else.
     *
     * @deprecated Use {@link #Item(ItemType, Item, String, String, String, String)}
     * instead.
     */
    @Deprecated
    public Item() {
    }

    /**
     * Create a new Item in a Project. This constructor creates a new Item or
     * subitem in a project or folder.
     *
     * @param type The type of file this Item should be.
     * @param title The name of this Item. e.g. "Main Theme".
     * @param shortDescription A short description of this Item. e.g. "This is
     * Main Theme of this project".
     * @param longDescription A long description of this item.
     * @param file The file of this Item.
     * @param uploader The person who is creating this item.
     */
    public Item(ItemType type, String title, String shortDescription, String longDescription, FileItem file, Employee uploader) {
        this.type = type;
        this.title = title;
        this.shortDescription = shortDescription;
        this.longDescription = longDescription;
        if (this.type == ItemType.FOLDER) {
            this.children = new ArrayList<Item>();
        } else {
            this.revisions = new ArrayList<Revision>();
            if (!(file == null || file.equals(""))) {
                this.revisions.add(new Revision(1, file, uploader, this));
            }
        }
    }

    // getters
    /**
     * @return The unique id number of this item.
     */
    public long getId() {
        return this.id;
    }

    /**
     * @return The itemtype of this item.
     */
    public ItemType getType() {
        return this.type;
    }

    /**
     * @return The folder containing this item.
     */
    public Item getParent() {
        return this.parent;
    }

    /**
     * @return A list containing all child items if this is a folder.
     */
    public Item[] getChildren() {
        return this.children.toArray(new Item[0]);
    }

    /**
     * @return The name of this item.
     */
    public String getTitle() {
        return this.title;
    }

    /**
     * @return The short description of this item.
     */
    public String getShortDescription() {
        return this.shortDescription;
    }

    /**
     * @return The long description of this item.
     */
    public String getLongDescription() {
        return this.longDescription;
    }

    /**
     * @return The latest revision of this item.
     */
    public int getLatestRevision() {
        return this.revisions.size();
    }

    /**
     * @return An array containing all revisions of this item.
     */
    public Revision[] getRevisions() {
        return this.revisions.toArray(new Revision[0]);
    }

    // methods
    /**
     * Returns a clone of this item. This method creates a clone of this item
     * and all of it's child items.
     *
     * @param parent The parent of this item.
     * @return A clone of this item.
     */
    public Item clone(Item parent) {
        try {
            Item arch = (Item) super.clone();
            arch.id = null;
            if (arch.getType() == ItemType.FOLDER) {
                arch.children = new ArrayList<Item>();
                for (Item i : this.children) {
                    arch.add(i.clone(arch));
                }
            }
            return arch;
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * Increments the revision of this item by one. This method increments the
     * latest revision of this item by one.
     *
     * @param file The file to be used as the new revision of this item.
     * @param uploader The person who is uploading this revision.
     */
    public void addRevision(FileItem file, Employee uploader) {
        this.revisions.add(new Revision(this.revisions.size() + 1, file, uploader, this));
    }

    /**
     * Finds and returns the requested revision. This method finds and returns
     * the requested revision.
     *
     * @param revision The revision number.
     * @return The matching revision.
     */
    public Revision getRevision(int revision) {
        for (Revision r : this.revisions) {
            if (r.getVersion() == revision) {
                return r;
            }
        }
        return null;
    }

    /**
     * Finds and returns a revision by its unique id. This method finds a
     * revision by its unique id number and returns it.
     *
     * @param id The id of the revidion to find.
     * @return The revision with the matching id.
     */
    public Revision findRevisionById(long id) {
        if (this.type == ItemType.FOLDER) {
            for (Item i : this.children) {
                Revision r = i.findRevisionById(id);
                if (r != null) {
                    return r;
                }
            }
        } else {
            for (Revision r : this.revisions) {
                if (r.getId() == id) {
                    return r;
                }
            }
        }
        return null;
    }

    /**
     * Adds an item to this folder. If this item is a folder, the item given as
     * a parameter will be added to this folder. If this item is not a folder,
     * nothing will happen.
     *
     * @param toBeAdded The item that should be added.
     */
    public void add(Item toBeAdded) {
        if (this.type == ItemType.FOLDER) {
            this.children.add(toBeAdded);
            toBeAdded.parent = this;
        }
    }

    /**
     * Removes an item from this folder. If this item is a folder, the item
     * given as a parameter will be removed from this folder. If this item is
     * not a folder, nothing will happen.
     *
     * @param toBeRemoved The item that should be added.
     */
    public void remove(Item toBeRemoved) {
        if (this.type == ItemType.FOLDER) {
            this.children.remove(toBeRemoved);
        }
    }

    /**
     * Moves this item to another parent folder. If the target item is a folder,
     * this item will be moved to this folder. That means this item will be
     * removed from it's parent folder and added to the target folder.
     *
     * @param target The folder to which this item should be moved.
     */
    public void move(Item target) {
        if (target.getType() == ItemType.FOLDER) {
            this.parent.remove(this);
            this.parent = target;
            target.add(this);
        }
    }

    /**
     * Returns the full path of this item. This method returns the full path of
     * this item starting with the first parent which has no parent item.
     *
     * @return The full path of this item.
     */
    public String fullPath() {
        return (this.parent == null ? "/" : this.parent.fullPath()) + this.title + (this.type == ItemType.FOLDER ? "/" : "");
    }

    /**
     * Returns an item containing the id given as a parameter. This method
     * searches for an item that has the id passed as a parameter.
     *
     * @param id The id of the item to find.
     * @return The item containing the id number.
     */
    public Item findItemById(long id) {
        if (this.id == id) {
            return this;
        }
        if (this.type == ItemType.FOLDER) {
            for (Item c : this.children) {
                Item i = c.findItemById(id);
                if (i != null) {
                    return i;
                }
            }
        }
        return null;
    }
    
    public void addSubItemsToList(List<Item> list) {
        if(this.type == ItemType.FOLDER) {
            for(Item i : this.children) {
                i.addSubItemsToList(list);
            }
        } else {
            list.add(this);
        }
    }
}
